如何实现 Dark Mode
•
# 如何实现 Dark Mode
## 实现
### Step1. 通过 CSS 媒体查询的 [prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) 功能来检测用户系统或代理的偏好设置
用户没有手动设置过 theme 时,theme 应该自动根据用户系统或代理的偏好进行切换。
```css
:root {
--color-font: #333;
}
@media (prefers-color-scheme: dark){
:root{
--color-font: #fff;
}
}
```
### Step2. 设置 class 覆盖用户系统或代理的偏好设置
当用户手动点击网页上的切换主题以后,使用自定义的 class 覆盖 @media 查询的用户系统偏好。并将用户主动选择的 theme 记录到 localStorage 中。
```css
.light{
--color-font: #333;
}
.dark{
--color-font: #fff;
}
<body class="dark"></body>
```
### Step3. 在页面内容加载前,检测用户本地 localStorage 中的 theme 设置
避免用户手动设置的 theme 与用户系统或代理的偏好设置不一致,导致页面在第一次加载时 theme 被覆盖导致的抖动。
```html
<html>
<body>
<script>
;(() => {
const preference = localStorage.getItem('${APPEARANCE_KEY}') || '${fallbackPreference}'
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
if (!preference || preference === 'auto' ? prefersDark : preference === 'dark')
document.documentElement.classList.add('dark')
})()
</script>
</body>
</html>
```
## FAQ
### 为什么 React、Vue、Svelet 都使用 localStorage 存储 theme,而不是用 cookie 呢?
看了一下 React、Vue、Svelet 官网的主题实现,发现他们都将 theme 存储在 localStorage 中,然后在 html 中靠前的位置插入一段检测 localStorage 的中 theme 的代码,从而避免水合不一致的问题。为什么不使用 cookie 呢?这样就可以在服务端 SSR 的时候获取到用户的配置了。
我个人认为,可能有以下原因:
- Cookie 会带到每个请求中造成不必要的浪费,也可能有一定的安全考虑
- Cookie 存储的时间有限,可能会因为用户清空缓存导致失效
- 根据 GDPR 法案的政策,使用 cookie 存储信息,即使是用户的偏好设置,不用与收集的目的也要告知和提示 (我觉得这个可能是最重要的,Meta 和 Goolge 似乎都因此被开出过天价罚单)
(https://github.com/vuejs/vitepress/blob/d3dd4bc93806f3bc7be5f29ad279978b4fd13c81/src/node/config.ts#L282)
(https://github.com/reactjs/react.dev/blob/db2dc7f9875f00688cd6ffc511abf5849c21952a/src/pages/_document.tsx#L37)
(https://github.com/sveltejs/svelte/blob/d5776c3ec38b98659081d21f3cd93960ebfddb91/sites/svelte.dev/src/app.html#L34)